Skip to content

fix(cli): resolve list/wait projects by repo URL, not directory name (COR-1577) - #137

Open
juangaitanv wants to merge 2 commits into
mainfrom
juan/cor-1577-resolve-by-repo
Open

fix(cli): resolve list/wait projects by repo URL, not directory name (COR-1577)#137
juangaitanv wants to merge 2 commits into
mainfrom
juan/cor-1577-resolve-by-repo

Conversation

@juangaitanv

Copy link
Copy Markdown
Contributor

Fixes COR-1577. Replaces #122, which grew well past the ticket; this is the fix on its own, and the hardening follows in a stacked PR.

The bug

corgea list and standalone corgea wait derived the project from the working-directory basename and sent it as an exact ?project= match. Where the stored name differs — the Bank of Hope case, directory dotnet-azure-web-tsb vs project bohappdev/dotnet-azure-web-tsblist --issues and wait exited 1, and plain list silently printed an empty table.

The fix

Resolve the canonical project from the git remote — GET /api/v1/projects?repo_url=<org/repo>, discovered upward from the CWD so a subdirectory works — then query the listing endpoints by the name the backend actually stores.

Three things make it safe on older and partially-onboarded backends:

  • The path re-check. The backend filters repo_url__icontains, so a query for acme/api also returns acme/api-v2 and the nested mirrors/acme/api. Every candidate is re-checked against the whole post-host path. The same check guards a pre-COR-1426 backend, which ignores the unknown param and returns all projects: none match, so we fall back instead of listing a stranger's scans.
  • The fallback is unchanged behavior. Unconfirmed, the query stays exactly what the pre-COR-1577 CLI sent (determine_project_name), so an old or not-yet-onboarded backend keeps working.
  • Empty is no longer silently fine. An unresolved project with no scans errors and names what was tried; a confirmed project with no scans still exits 0, so CI polling is unaffected.

Escape hatches: --project-name queries an exact name and skips resolution entirely; --repo resolves from a given slug or URL instead of the remote. Both are on list and wait.

Also removes the client-side scan.project == cwd_basename filter — the server already filtered, and that pass discarded every resolved scan.

Deliberately not here

Kept out to hold this to the ticket; all of it lands in the stacked follow-up:

  • pagination of /projects (page 1 only today — see COR-1729, which would let the CLI drop both the walk and the re-check)
  • host disambiguation for the same org/repo on two forges
  • hard-vs-soft failure for malformed /projects envelopes
  • percent-encoding the project name into /issues (pre-existing)
  • --project-id, SCA selector scoping, argument structs

Verification

cargo build, cargo clippy --all-targets (clean), cargo fmt --check, and the full suite under CI=1 GITHUB_ACTIONS=true — 537 tests.

16 new e2e tests cover: the canonical name driving /scans and /issues (asserted on the request the CLI sent, from a checkout named build-123, so a fallback cannot pass); resolution from a subdirectory; --repo; the miss naming the repo with no table; confirmed-empty exiting 0; the legacy fallback name; --project-name skipping resolution; the JSON envelope preceding the miss; --scan-id skipping resolution; and the post-scan corgea scan path resolving nothing.

Size

429 lines of production code (~340 excluding comments and blank lines), 825 of tests.

Comment thread src/utils/api.rs Outdated
Comment thread src/utils/api.rs Outdated
Comment thread src/utils/api.rs Outdated
Comment thread src/list.rs
Comment thread src/list.rs
COR-1577. `corgea list` and standalone `corgea wait` derived the project
from the working-directory basename and sent it as an exact `?project=`
match. Where the stored name differs — the Bank of Hope case, dir
`dotnet-azure-web-tsb` vs project `bohappdev/dotnet-azure-web-tsb` —
`list --issues` and `wait` exited 1 and plain `list` silently printed an
empty table.

Both now resolve the canonical project from the git remote:
`GET /api/v1/projects?repo_url=<org/repo>`, discovered upward from the CWD
so a subdirectory works too, then query the listing endpoints by the name
the backend actually stores.

- The backend filters `repo_url__icontains`, so every candidate is
  re-checked against the whole post-host path. That also guards a
  pre-COR-1426 backend, which ignores the unknown param and returns ALL
  projects: none match, and we fall back rather than list a stranger's
  scans.
- Unconfirmed, the query stays exactly what the pre-COR-1577 CLI sent, so
  an old or not-yet-onboarded backend keeps working.
- `--project-name` queries an exact name and skips resolution entirely;
  `--repo` resolves from a given slug or URL instead of the remote.
- An unresolved project with no scans is now an error naming what was
  tried, instead of an empty table; a confirmed project with no scans
  still exits 0 so CI polling is unaffected.
- The client-side `scan.project == cwd_basename` filter is gone — the
  server already filtered, and that pass discarded every resolved scan.
@juangaitanv
juangaitanv force-pushed the juan/cor-1577-resolve-by-repo branch from 0184a26 to 1a543f4 Compare July 30, 2026 13:33
Comment thread src/wait.rs
);
std::process::exit(1);
}
format!("{}/project/{}?scan_id={}", config.get_url(), name, scan_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The canonical names introduced by this PR contain / (the new fixture is bohappdev/dotnet-azure-web-tsb), but this inserts that name into a single URL path segment without encoding it. src/scan.rs:63-74 already documents and enforces the same route contract with urlencoding::encode, specifically because a raw slash routes to the wrong project page. The new test at tests/wait_resolution.rs:93 currently locks in the broken raw URL, so the primary wait result link for COR-1577 will not target the resolved project. Encode the segment and change the test to expect %2F.

Suggested change
format!("{}/project/{}?scan_id={}", config.get_url(), name, scan_id)
format!(
"{}/project/{}?scan_id={}",
config.get_url(),
urlencoding::encode(name),
scan_id
)

Comment thread src/wait.rs
// A scan id plus the project id from the upload response leaves nothing to
// resolve: everything below keys off the scan, and the id-form URL is
// already known.
let resolved = if scan_id.is_some() && project_id.is_some() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shortcut still makes standalone corgea wait <scan-id> run repo resolution because that CLI path has no project_id. A duplicate/ambiguous repo mapping, malformed /projects payload, or resolver page cap therefore exits in resolve_project_or_exit before line 57 ever checks the explicitly supplied scan. The new wait_with_scan_id_does_not_list_scans test misses this because its /projects stub is successful and it only asserts that /scans was skipped. The scan endpoint already returns both status and project, so make the scan-ID path use that response (honoring an explicit --project-name if supplied) and reserve repo resolution for the scan-less path; add a regression where /projects would fail/ambiguous but wait scan-123 still succeeds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant